/**
 * Plateau.java - Plateau qui contient les cases du jeu
 * 
 * @author BOURGE Denis && BEGORRE Alexandre
 * @version 1.0
 */

package elements.plateau;

import java.awt.GridLayout;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.JOptionPane;
import javax.swing.JPanel;

import utils.InfoException;
import elements.dictionnaire.Dictionnaire;
import elements.jetons.Jeton;
import elements.jetons.Rack;

@SuppressWarnings("serial")
public class Plateau extends JPanel {

	private GridLayout		gridLayout;
	private Case[][]		matrice;
	public static final int	TAILLE_LETTRE		= 32;
	public static final int	TAILLE_PLATEAU		= 15;
	public static final int	ESPACEMENT_PLATEAU	= 2;

	/**
	 * Constructeur du plateau
	 */
	public Plateau() {

		/* On crée le layout qui contiendra toutes les cases */
		gridLayout = new GridLayout(TAILLE_PLATEAU, TAILLE_PLATEAU, ESPACEMENT_PLATEAU, ESPACEMENT_PLATEAU);
		this.setLayout(gridLayout);

		/* Création des cases */
		matrice = new Case[TAILLE_PLATEAU][TAILLE_PLATEAU];
		String[] ligne = new String[TAILLE_PLATEAU];

		/* Lecture du fichier "data/board/board.def" */
		try {
			BufferedReader entree = new BufferedReader(new FileReader("data/board/board.def"));

			String tmp;
			int i = 0;

			do {
				tmp = entree.readLine();
				if (tmp != null) {
					if (tmp.startsWith("//"))
					;
					else if (tmp.equals(""))
					;
					else {
						if (i != TAILLE_PLATEAU) {
							ligne[i] = tmp;
							i++;
						}
					}
				}
			} while (tmp != null);

			entree.close();

		} catch (IOException e) {
			// TODO Catch
			e.printStackTrace();
			System.exit(1);
		}

		/* Initialisation des cases */
		for (int j = 0; j < TAILLE_PLATEAU; j++) {
			for (int i = 0; i < TAILLE_PLATEAU; i++) {

				switch (ligne[j].charAt(i)) {
					case '$':
						matrice[i][j] = new Case(null, CaseType.MOT_TRIPLE, "data/graphics/cases/caseMotTriple.png");
						break;

					case '-':
						matrice[i][j] = new Case(null, CaseType.LET_DOUBLE, "data/graphics/cases/caseLettreDouble.png");
						break;

					case '+':
						matrice[i][j] = new Case(null, CaseType.LET_TRIPLE, "data/graphics/cases/caseLettreTriple.png");
						break;

					case '%':
						matrice[i][j] = new Case(null, CaseType.MOT_DOUBLE, "data/graphics/cases/caseMotDouble.png");
						break;

					case '*':
						matrice[i][j] = new Case(null, CaseType.DEPART, "data/graphics/cases/caseDepart.png");
						break;

					default:
						matrice[i][j] = new Case(null, CaseType.NORMALE, "data/graphics/cases/caseNormale.png");
						break;
				}

				this.add(matrice[i][j].getImage());
			}
		}

	}

	/**
	 * Retourne une copie de la matrice de cases.
	 * 
	 * @return Une copie de la matrice de cases.
	 */
	public Case[][] getCopieMatrice() {

		Case[][] tmp = new Case[TAILLE_PLATEAU][TAILLE_PLATEAU];

		for (int j = 0; j < TAILLE_PLATEAU; j++) {
			for (int i = 0; i < TAILLE_PLATEAU; i++) {
				tmp[i][j] = new Case(matrice[i][j].getJeton(), matrice[i][j].getType(), matrice[i][j].getPathToImage());
			}
		}

		return tmp;
	}

	/**
	 * Modifie la matrice courante en fonction d'une copie de celle-ci
	 * 
	 * @param tmp
	 *            Matrice source
	 */
	public void setMatrice(Case[][] tmp) {

		for (int j = 0; j < TAILLE_PLATEAU; j++) {
			for (int i = 0; i < TAILLE_PLATEAU; i++) {
				matrice[i][j].setType(tmp[i][j].getType());
				matrice[i][j].chargerJeton(tmp[i][j].getJeton());
			}
		}
	}

	/**
	 * Met à jour le plateau (recharge les images)
	 */
	public void mettreAJour() {

		for (int j = 0; j < TAILLE_PLATEAU; j++) {
			for (int i = 0; i < TAILLE_PLATEAU; i++) {
				matrice[i][j].mettreAJour();
			}
		}
	}

	/**
	 * Fonction de débuggage - Affiche le plateau dans la console
	 */
	public static void afficherConsole(Case[][] mat) {

		for (int j = 0; j < TAILLE_PLATEAU; j++) {
			for (int i = 0; i < TAILLE_PLATEAU; i++) {
				if (mat[i][j].getJeton() != null) {
					System.out.print(mat[i][j].getJeton().getLettre());
				} else {
					System.out.print("_");
				}

				if (i == TAILLE_PLATEAU - 1) {
					System.out.println("");
				}
			}
		}
	}

	/**
	 * Teste si tous les mots sur le plateau existe
	 * 
	 * @param mat
	 *            Matrice de cases
	 * @return Oui ou non
	 */
	public boolean testerMatrice(Case[][] mat, Dictionnaire dico) {

		boolean retour = true;

		List<String> listeMots = new ArrayList<String>();

		String tmp = "";

		/* Test horizontal */
		for (int j = 0; j < TAILLE_PLATEAU; j++) {
			for (int i = 0; i < TAILLE_PLATEAU; i++) {

				if (mat[i][j].getJeton() != null) {
					tmp += mat[i][j].getJeton().getLettre();
				} else {
					if (tmp.length() != 0 && tmp.length() != 1) {
						listeMots.add(tmp);
					}
					tmp = "";
				}

			}
		}

		/* Test vertical */
		for (int i = 0; i < TAILLE_PLATEAU; i++) {
			for (int j = 0; j < TAILLE_PLATEAU; j++) {

				if (mat[i][j].getJeton() != null) {
					tmp += mat[i][j].getJeton().getLettre();
				} else {
					if (tmp.length() != 0 && tmp.length() != 1) {
						listeMots.add(tmp);
					}
					tmp = "";
				}

			}
		}

		/* Vérification des mots */
		Iterator<String> it = listeMots.iterator();
		while (it.hasNext()) {
			String str = it.next();
			if (!dico.chercherMot(str)) {
				JOptionPane.showMessageDialog(null, "Le mot \"" + str + "\" n'existe pas !", "HE !", JOptionPane.ERROR_MESSAGE);
				retour = false;
			}
		}

		return retour;
	}

	/**
	 * Ajoute un mot sur le plateau (avec tout plein de tests et d'exceptions)
	 * 
	 * @param x
	 *            Position X
	 * @param y
	 *            Position Y
	 * @param horiz
	 *            Horizontal ou non
	 * @param mot
	 *            Mot à placer
	 * @param rackJoueur
	 *            Rack du joueur
	 * @param numTour
	 *            Numéro du tour actuel
	 * @param dico
	 *            Dictionnaire pour rechercher si le mot existe
	 * @return Le nombre de points du coup
	 * @throws InfoException
	 *             Messages explicatifs sur l'erreur
	 */
	public int ajouterMot(int x, int y, boolean horiz, String mot,
			Rack rackJoueur, int numTour, Dictionnaire dico)
			throws InfoException {

		/* On copie la matrice et le rack actuels dans des variables temporaires */
		Case[][] matriceTmp = this.getCopieMatrice();
		Rack rackTmp = rackJoueur.getCopie();

		int tailleMot = mot.length();
		int currPoints = 0;

		int nbMotDouble = 0;
		int nbMotTriple = 0;
		Map<Character, Integer> nbLetDouble = new HashMap<Character, Integer>();
		Map<Character, Integer> nbLetTriple = new HashMap<Character, Integer>();

		/* Si la position X et Y sont bien dans le plateau */
		if (x >= 0 && x < TAILLE_PLATEAU && y >= 0 && y < TAILLE_PLATEAU) {

			/* Mode horizontal */
			if (horiz) {

				/* Si le mot ne dépasse pas du plateau */
				if (x + tailleMot <= TAILLE_PLATEAU) {

					/* Si il n'y à aucune lettre (et tour > 1) */
					if (numTour > 1) {
						int nbLettres = 0;
						for (int i = 0; i < tailleMot; i++) {
							if (matriceTmp[x + i][y].getJeton() != null) nbLettres++;
						}

						if (nbLettres == 0) throw new InfoException("Vous ne pouvez pas mettre de mots ici ! (il faut des jetons sur le plateau...)");
						else if (nbLettres == tailleMot) throw new InfoException("Vous ne pouvez pas mettre de mots sur un mot !");
					}

					for (int i = 0; i < tailleMot; i++) {

						/* Si il n'y a pas de jeton à cet endroit */
						if (matriceTmp[x + i][y].getJeton() == null) {

							/* Si il y a bien la lettre recherchée dans le rack */
							if (rackTmp.estDans(mot.charAt(i))) {

								/*
								 * Alors, on peut enfin mettre le jeton dans la
								 * matrice !
								 */
								matriceTmp[x + i][y].chargerJeton(new Jeton(mot.charAt(i), Dictionnaire.valeurLettre(mot.charAt(i))));
								switch (matriceTmp[x + i][y].getType()) {
									case MOT_DOUBLE:
										nbMotDouble++;
										break;
									case MOT_TRIPLE:
										nbMotTriple++;
										break;
									case LET_DOUBLE:
										if (nbLetDouble.containsKey(matriceTmp[x + i][y].getJeton().getLettre())) {
											nbLetDouble.put(matriceTmp[x + i][y].getJeton().getLettre(), nbLetDouble.get(matriceTmp[x + i][y].getJeton().getLettre()) + 1);
										} else {
											nbLetDouble.put(matriceTmp[x + i][y].getJeton().getLettre(), 1);
										}
										break;
									case LET_TRIPLE:
										if (nbLetTriple.containsKey(matriceTmp[x + i][y].getJeton().getLettre())) {
											nbLetTriple.put(matriceTmp[x + i][y].getJeton().getLettre(), nbLetTriple.get(matriceTmp[x + i][y].getJeton().getLettre()) + 1);
										} else {
											nbLetTriple.put(matriceTmp[x + i][y].getJeton().getLettre(), 1);
										}
										break;
									default:
										currPoints += matriceTmp[x + i][y].getJeton().getPoints();
										break;
								}

							} else {
								throw new InfoException("Il vous manque des lettres !");
							}
						}

						/*
						 * S'il y a un jeton sur le plateau à cet endroit et
						 * qu'il est bon
						 */
						else {
							if (matriceTmp[x + i][y].getJeton().getLettre() == mot.charAt(i)) {

								/* On passe au suivant */
								switch (matriceTmp[x + i][y].getType()) {
									case MOT_DOUBLE:
										nbMotDouble++;
										break;
									case MOT_TRIPLE:
										nbMotTriple++;
										break;
									case LET_DOUBLE:
										if (nbLetDouble.containsKey(matriceTmp[x + i][y].getJeton().getLettre())) {
											nbLetDouble.put(matriceTmp[x + i][y].getJeton().getLettre(), nbLetDouble.get(matriceTmp[x + i][y].getJeton().getLettre()) + 1);
										} else {
											nbLetDouble.put(matriceTmp[x + i][y].getJeton().getLettre(), 1);
										}
										break;
									case LET_TRIPLE:
										if (nbLetTriple.containsKey(matriceTmp[x + i][y].getJeton().getLettre())) {
											nbLetTriple.put(matriceTmp[x + i][y].getJeton().getLettre(), nbLetTriple.get(matriceTmp[x + i][y].getJeton().getLettre()) + 1);
										} else {
											nbLetTriple.put(matriceTmp[x + i][y].getJeton().getLettre(), 1);
										}
										break;
									default:
										currPoints += matriceTmp[x + i][y].getJeton().getPoints();
										break;
								}
							} else {
								throw new InfoException("Il y a déja des lettres à cet endroit ! (qui ne correspondent pas à votre mot)");
							}
						}
					}

				} else
					throw new InfoException("Mot trop long !");
			}

			/* Mode vertical */
			else {

				/* Si le mot ne dépasse pas du plateau */
				if (y + tailleMot <= TAILLE_PLATEAU) {

					/* Si il n'y à aucune lettre (et tour > 1) */
					if (numTour > 1) {
						int nbLettres = 0;
						for (int i = 0; i < tailleMot; i++) {
							if (matriceTmp[x][y + i].getJeton() != null) nbLettres++;
						}

						if (nbLettres == 0) throw new InfoException("Vous ne pouvez pas mettre de mots ici ! (il faut des jetons sur le plateau...)");
						else if (nbLettres == tailleMot) throw new InfoException("Vous ne pouvez pas mettre de mots sur un mot !");
					}

					for (int i = 0; i < tailleMot; i++) {

						/* Si il n'y a pas de jeton à cet endroit */
						if (matriceTmp[x][y + i].getJeton() == null) {

							/* Si il y a bien la lettre recherchée dans le rack */
							if (rackTmp.estDans(mot.charAt(i))) {

								/*
								 * Alors on peut mettre le jeton dans la matrice
								 */
								matriceTmp[x][y + i].chargerJeton(new Jeton(mot.charAt(i), Dictionnaire.valeurLettre(mot.charAt(i))));
								switch (matriceTmp[x][y + i].getType()) {
									case MOT_DOUBLE:
										nbMotDouble++;
										break;
									case MOT_TRIPLE:
										nbMotTriple++;
										break;
									case LET_DOUBLE:
										if (nbLetDouble.containsKey(matriceTmp[x][y + i].getJeton().getLettre())) {
											nbLetDouble.put(matriceTmp[x][y + i].getJeton().getLettre(), nbLetDouble.get(matriceTmp[x][y + i].getJeton().getLettre()) + 1);
										} else {
											nbLetDouble.put(matriceTmp[x][y + i].getJeton().getLettre(), 1);
										}
										break;
									case LET_TRIPLE:
										if (nbLetTriple.containsKey(matriceTmp[x][y + i].getJeton().getLettre())) {
											nbLetTriple.put(matriceTmp[x][y + i].getJeton().getLettre(), nbLetTriple.get(matriceTmp[x][y + i].getJeton().getLettre()) + 1);
										} else {
											nbLetTriple.put(matriceTmp[x][y + i].getJeton().getLettre(), 1);
										}
										break;
									default:
										currPoints += matriceTmp[x][y + i].getJeton().getPoints();
										break;
								}
							} else {
								throw new InfoException("Il vous manque des lettres !");
							}
						}

						/*
						 * S'il y a un jeton sur le plateau à cet endroit et
						 * qu'il est bon...
						 */
						else {
							if (matriceTmp[x][y + i].getJeton().getLettre() == mot.charAt(i)) {

								/* On passe au suivant */
								switch (matriceTmp[x][y + i].getType()) {
									case MOT_DOUBLE:
										nbMotDouble++;
										break;
									case MOT_TRIPLE:
										nbMotTriple++;
										break;
									case LET_DOUBLE:
										if (nbLetDouble.containsKey(matriceTmp[x][y + i].getJeton().getLettre())) {
											nbLetDouble.put(matriceTmp[x][y + i].getJeton().getLettre(), nbLetDouble.get(matriceTmp[x][y + i].getJeton().getLettre()) + 1);
										} else {
											nbLetDouble.put(matriceTmp[x][y + i].getJeton().getLettre(), 1);
										}
										break;
									case LET_TRIPLE:
										if (nbLetTriple.containsKey(matriceTmp[x][y + i].getJeton().getLettre())) {
											nbLetTriple.put(matriceTmp[x][y + i].getJeton().getLettre(), nbLetTriple.get(matriceTmp[x][y + i].getJeton().getLettre()) + 1);
										} else {
											nbLetTriple.put(matriceTmp[x][y + i].getJeton().getLettre(), 1);
										}
										break;
									default:
										currPoints += matriceTmp[x][y + i].getJeton().getPoints();
										break;
								}
							} else {
								throw new InfoException("Il y a déja des lettres à cet endroit ! (qui ne correspondent pas à votre mot)");
							}
						}
					}

				} else
					throw new InfoException("Mot trop long !");

			}
		} else
			throw new InfoException("x ou y en dehors du plateau !");

		/* Vérification finale - Test des mots sur le plateau */
		if (testerMatrice(matriceTmp, dico)) {

			/* Ajout des points */

			Iterator<Entry<Character, Integer>> it = nbLetDouble.entrySet().iterator();
			while (it.hasNext()) {
				Map.Entry<Character, Integer> e = it.next();
				currPoints += 2 * Dictionnaire.valeurLettre(e.getKey()) * e.getValue();
			}

			Iterator<Entry<Character, Integer>> it2 = nbLetTriple.entrySet().iterator();
			while (it2.hasNext()) {
				Map.Entry<Character, Integer> e = it2.next();
				currPoints += 3 * Dictionnaire.valeurLettre(e.getKey()) * e.getValue();
			}

			while (nbMotDouble != 0) {
				currPoints *= 2;
				nbMotDouble--;
			}

			while (nbMotTriple != 0) {
				currPoints *= 3;
				nbMotTriple--;
			}

			this.setMatrice(matriceTmp);
			rackJoueur.setRack(rackTmp);
			this.mettreAJour();

			return currPoints;

		} else
			throw new InfoException("Après l'ajout d'un mot, un ou plusieurs mots ne veulent rien dire !");
	}

	/**
	 * Récupère la case [i, j] de la matrice de cases.
	 * 
	 * @param i
	 *            Position i
	 * @param j
	 *            Position j
	 * @return La case voulue
	 */
	public Case getMatrice(int i, int j) {

		return matrice[i][j];
	}
}